<template>
  <li :class="getClass">
    <template v-if="!getCollapse">
      <div
        :class="`${prefixCls}-submenu-title`"
        @click.stop="handleClick"
        :style="getItemStyle"
      >
        <slot name="title"></slot>
        <Icon
          icon="eva:arrow-ios-downward-outline"
          :size="14"
          :class="`${prefixCls}-submenu-title-icon`"
        />
      </div>
      <CollapseTransition>
        <ul :class="prefixCls" v-show="opened">
          <slot></slot>
        </ul>
      </CollapseTransition>
    </template>

    <Popover
      placement="right"
      :overlayClassName="`${prefixCls}-menu-popover`"
      v-else
      :visible="getIsOpend"
      @visibleChange="handleVisibleChange"
      :overlayStyle="getOverlayStyle"
      :align="{ offset: [0, 0] }"
    >
      <div :class="getSubClass" v-bind="getEvents(false)">
        <div
          :class="[
            {
              [`${prefixCls}-submenu-popup`]: !getParentSubMenu,
              [`${prefixCls}-submenu-collapsed-show-tit`]: collapsedShowTitle,
            },
          ]"
        >
          <slot name="title"></slot>
        </div>
        <Icon
          v-if="getParentSubMenu"
          icon="eva:arrow-ios-downward-outline"
          :size="14"
          :class="`${prefixCls}-submenu-title-icon`"
        />
      </div>
      <!-- eslint-disable-next-line -->
      <template #content v-show="opened">
        <div v-bind="getEvents(true)">
          <ul
            :class="[
              prefixCls,
              `${prefixCls}-${getTheme}`,
              `${prefixCls}-popup`,
            ]"
          >
            <slot></slot>
          </ul>
        </div>
      </template>
    </Popover>
  </li>
</template>

<script lang="ts">
import type { CSSProperties, PropType } from 'vue'
import type { SubMenuProvider } from './types'
import {
  defineComponent,
  computed,
  unref,
  getCurrentInstance,
  toRefs,
  reactive,
  provide,
  onBeforeMount,
  inject,
} from 'vue'
import { useDesign } from '@/hooks/web/useDesign'
import { useMenuItem } from './useMenu'
import { useSimpleRootMenuContext } from './useSimpleMenuContext'
import { CollapseTransition } from '@/components/Transition'
import Icon from '@/components/Icon'
import { Popover } from 'ant-design-vue'
import { isBoolean, isObject } from '@admin/utils'
import { mitt } from '@admin/utils'

const DELAY = 200
export default defineComponent({
  name: 'SubMenu',
  components: {
    Icon,
    CollapseTransition,
    Popover,
  },
  props: {
    name: {
      type: [String, Number] as PropType<string | number>,
      required: true,
    },
    disabled: { type: Boolean },
    collapsedShowTitle: { type: Boolean },
  },
  setup(props) {
    const instance = getCurrentInstance()

    const state = reactive({
      active: false,
      opened: false,
    })

    const data = reactive({
      timeout: null as TimeoutHandle | null,
      mouseInChild: false,
      isChild: false,
    })

    const { getParentSubMenu, getItemStyle, getParentMenu, getParentList } =
      useMenuItem(instance)

    const { prefixCls } = useDesign('menu')

    const subMenuEmitter = mitt()

    const { rootMenuEmitter } = useSimpleRootMenuContext()

    const {
      addSubMenu: parentAddSubmenu,
      removeSubMenu: parentRemoveSubmenu,
      removeAll: parentRemoveAll,
      getOpenNames: parentGetOpenNames,
      isRemoveAllPopup,
      sliceIndex,
      level,
      props: rootProps,
      handleMouseleave: parentHandleMouseleave,
    } = inject<SubMenuProvider>(`subMenu:${getParentMenu.value?.uid}`)!

    const getClass = computed(() => {
      return [
        `${prefixCls}-submenu`,
        {
          [`${prefixCls}-item-active`]: state.active,
          [`${prefixCls}-opened`]: state.opened,
          [`${prefixCls}-submenu-disabled`]: props.disabled,
          [`${prefixCls}-submenu-has-parent-submenu`]: unref(getParentSubMenu),
          [`${prefixCls}-child-item-active`]: state.active,
        },
      ]
    })

    const getAccordion = computed(() => rootProps.accordion)
    const getCollapse = computed(() => rootProps.collapse)
    const getTheme = computed(() => rootProps.theme)

    const getOverlayStyle = computed((): CSSProperties => {
      return {
        minWidth: '200px',
      }
    })

    const getIsOpend = computed(() => {
      const name = props.name
      if (unref(getCollapse)) {
        return parentGetOpenNames().includes(name)
      }
      return state.opened
    })

    const getSubClass = computed(() => {
      const isActive = rootProps.activeSubMenuNames.includes(props.name)
      return [
        `${prefixCls}-submenu-title`,
        {
          [`${prefixCls}-submenu-active`]: isActive,
          [`${prefixCls}-submenu-active-border`]: isActive && level === 0,
          [`${prefixCls}-submenu-collapse`]: unref(getCollapse) && level === 0,
        },
      ]
    })

    function getEvents(deep: boolean) {
      if (!unref(getCollapse)) {
        return {}
      }
      return {
        onMouseenter: handleMouseenter,
        onMouseleave: () => handleMouseleave(deep),
      }
    }

    function handleClick() {
      const { disabled } = props
      if (disabled || unref(getCollapse)) return
      const opened = state.opened

      if (unref(getAccordion)) {
        const { uidList } = getParentList()
        rootMenuEmitter.emit('on-update-opened', {
          opend: false,
          parent: instance?.parent,
          uidList: uidList,
        })
      } else {
        rootMenuEmitter.emit('open-name-change', {
          name: props.name,
          opened: !opened,
        })
      }
      state.opened = !opened
    }

    function handleMouseenter() {
      const disabled = props.disabled
      if (disabled) return

      subMenuEmitter.emit('submenu:mouse-enter-child')

      const index = parentGetOpenNames().findIndex(
        (item) => item === props.name,
      )

      sliceIndex(index)

      const isRoot = level === 0 && parentGetOpenNames().length === 2
      if (isRoot) {
        parentRemoveAll()
      }
      data.isChild = parentGetOpenNames().includes(props.name)
      clearTimeout(data.timeout!)
      data.timeout = setTimeout(() => {
        parentAddSubmenu(props.name)
      }, DELAY)
    }

    function handleMouseleave(deepDispatch = false) {
      const parentName = getParentMenu.value?.props.name
      if (!parentName) {
        isRemoveAllPopup.value = true
      }

      if (parentGetOpenNames().slice(-1)[0] === props.name) {
        data.isChild = false
      }

      subMenuEmitter.emit('submenu:mouse-leave-child')
      if (data.timeout) {
        clearTimeout(data.timeout!)
        data.timeout = setTimeout(() => {
          if (isRemoveAllPopup.value) {
            parentRemoveAll()
          } else if (!data.mouseInChild) {
            parentRemoveSubmenu(props.name)
          }
        }, DELAY)
      }
      if (deepDispatch) {
        if (getParentSubMenu.value) {
          parentHandleMouseleave?.(true)
        }
      }
    }

    onBeforeMount(() => {
      subMenuEmitter.on('submenu:mouse-enter-child', () => {
        data.mouseInChild = true
        isRemoveAllPopup.value = false
        clearTimeout(data.timeout!)
      })
      subMenuEmitter.on('submenu:mouse-leave-child', () => {
        if (data.isChild) return
        data.mouseInChild = false
        clearTimeout(data.timeout!)
      })

      rootMenuEmitter.on(
        'on-update-opened',
        (data: boolean | (string | number)[] | Recordable) => {
          if (unref(getCollapse)) return
          if (isBoolean(data)) {
            state.opened = data
            return
          }
          if (isObject(data) && rootProps.accordion) {
            const { opend, parent, uidList } = data as Recordable
            if (parent === instance?.parent) {
              state.opened = opend
            } else if (!uidList.includes(instance?.uid)) {
              state.opened = false
            }
            return
          }

          if (props.name && Array.isArray(data)) {
            state.opened = (data as (string | number)[]).includes(props.name)
          }
        },
      )

      rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => {
        if (instance?.uid) {
          state.active = data.includes(instance?.uid)
        }
      })
    })

    function handleVisibleChange(visible: boolean) {
      state.opened = visible
    }

    // provide
    provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
      addSubMenu: parentAddSubmenu,
      removeSubMenu: parentRemoveSubmenu,
      getOpenNames: parentGetOpenNames,
      removeAll: parentRemoveAll,
      isRemoveAllPopup,
      sliceIndex,
      level: level + 1,
      handleMouseleave,
      props: rootProps,
    })

    return {
      getClass,
      prefixCls,
      getCollapse,
      getItemStyle,
      handleClick,
      handleVisibleChange,
      getParentSubMenu,
      getOverlayStyle,
      getTheme,
      getIsOpend,
      getEvents,
      getSubClass,
      ...toRefs(state),
      ...toRefs(data),
    }
  },
})
</script>
